package com.yanx.common.utils;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 基于 JDK 8 time包的时间工具类
 *
 * @author gotanks
 * @version 2.3
 * @since 2021-07-16
 */
public final class DateTime_ {

    /**
     * 获取默认时间格式: yyyy-MM-dd HH:mm:ss
     */
    private static final TimeFormat DEFAULT_FORMAT = TimeFormat.DATETIME_LINE;

    private DateTime_() {
        // no construct function
    }

    /*============================转LocalDateTime============================*/

    /**
     * String 转LocalDateTime
     *
     * @param timeStr 日期字符串
     * @return LocalDateTime
     */
    public static LocalDateTime parseDateTime(CharSequence timeStr) {
        if (timeStr == null) {
            return null;
        }
        String timeString = timeStr.toString();
        TimeFormat format = null;
        if (timeStr.length() > 10) {
            if (timeString.contains(Separation.LINE.separation)) {
                format = DEFAULT_FORMAT;
            } else if (timeString.contains(Separation.SLASH.separation)) {
                format = TimeFormat.DATETIME_SLASH;
            } else if (timeString.contains(Separation.BACKSLASH.separation)) {
                format = TimeFormat.DATETIME_BACKSLASH;
            } else {
                format = TimeFormat.DATETIME_NONE;
            }
        } else if (timeStr.length() == 10) {
            if (timeString.contains(Separation.LINE.separation)) {
                format = TimeFormat.DATE_LINE;
            } else if (timeString.contains(Separation.SLASH.separation)) {
                format = TimeFormat.DATE_SLASH;
            } else if (timeString.contains(Separation.BACKSLASH.separation)) {
                format = TimeFormat.DATE_BACKSLASH;
            }
        } else if (timeStr.length() == 8) {
            if (timeString.contains(Separation.COLON.separation)) {
                format = TimeFormat.TIME_COLON;
            } else {
                format = TimeFormat.DATE_NONE;
            }
        } else if (timeStr.length() == 7) {
            if (timeString.contains(Separation.LINE.separation)) {
                format = TimeFormat.DATE_MONTH_LINE;
            } else if (timeString.contains(Separation.SLASH.separation)) {
                format = TimeFormat.DATE_MONTH_SLASH;
            } else if (timeString.contains(Separation.BACKSLASH.separation)) {
                format = TimeFormat.DATE_MONTH_BACKSLASH;
            }
        } else if (timeStr.length() == 6) {
            format = TimeFormat.DATE_MONTH_NONE;
        }
        return parseDateTime(timeStr, format);
    }

    /**
     * 批量时间转换
     *
     * @param strDateTimeList 日期字符串集合
     * @return LocalDateTime
     */
    public static List<LocalDateTime> parseDateTime(List<String> strDateTimeList) {
        return strDateTimeList.stream()
                .map(strDateTime -> parseDateTime(strDateTime))
                .collect(Collectors.toList());
    }

    /**
     * String 转LocalDateTime
     *
     * @param timeStr 日期字符串
     * @param pattern 时间格式
     * @return LocalDateTime
     */
    public static LocalDateTime parseDateTime(CharSequence timeStr, String pattern) {
        if (timeStr == null) {
            return null;
        }
        return LocalDateTime.parse(timeStr, getFormatter(pattern));
    }

    public static void main(String[] args) {
        LocalDateTime localDateTime = DateTime_.parseDateTime("11:11:11", TimeFormat.TIME_COLON);
        LocalDateTime localDateTime2 = DateTime_.parseDateTime("2020-10");
        LocalDateTime localDateTime3 = DateTime_.parseDateTime("202009");
        System.out.println(localDateTime);
        System.out.println(localDateTime2.toLocalDate());
        System.out.println(localDateTime3);
        System.out.println(DateTime_.parseDateTime(1601230123L));
        System.out.println(DateTime_.parseDateTime(1601230123100L));
        System.out.println(DateTime_.parseDateTime(DateTime_.current()));
        System.out.println(DateTime_.parseDateTime(DateTime_.currentSecond()));
    }

    /**
     * String 转LocalDateTime
     *
     * @param timeStr 日期字符串
     * @param format  时间格式
     * @return LocalDateTime
     */
    public static LocalDateTime parseDateTime(CharSequence timeStr, TimeFormat format) {
        return parseDateTime(timeStr, format.pattern);
    }

    /**
     * Date 转 LocalDateTime
     *
     * @param date 日期
     * @return LocalDateTime
     */
    public static LocalDateTime parseDateTime(Date date) {
        if (date == null) {
            return null;
        }
        return instantToZonedDateTime(date.toInstant()).toLocalDateTime();
    }

    /**
     * long 转 LocalDateTime
     *
     * @param timestamp 毫秒
     * @return LocalDateTime
     */
    public static LocalDateTime parseDateTime(long timestamp) {
        return longToZonedDateTime(timestamp).toLocalDateTime();
    }

    //内部方法
    private static ZonedDateTime instantToZonedDateTime(Instant instant) {
        return instant.atZone(defaultZoneOffset());
    }

    private static ZoneOffset defaultZoneOffset() {
        return ZoneOffset.ofHours(8);
    }

    private static ZonedDateTime longToZonedDateTime(long timestamp) {
        Instant instant = timestamp > 10_000_000_000L
                ? Instant.ofEpochMilli(timestamp)
                : Instant.ofEpochSecond(timestamp);
        return instantToZonedDateTime(instant);
    }


    /*============================转LocalDate============================*/

    /**
     * String 转LocalDate
     *
     * @param timeStr
     * @return LocalDate
     */
    public static LocalDate parseDate(CharSequence timeStr) {
        return parseDateTime(timeStr).toLocalDate();
    }

    /**
     * 批量时间转换
     *
     * @param strDateList
     * @return LocalDate
     */
    public static List<LocalDate> parseDate(List<String> strDateList) {
        return strDateList.stream()
                .map(strDate -> parseDate(strDate))
                .collect(Collectors.toList());
    }

    /**
     * String 转LocalDate
     *
     * @param timeStr
     * @param pattern 时间格式
     * @return LocalDate
     */
    public static LocalDate parseDate(CharSequence timeStr, String pattern) {
        if (timeStr == null) {
            return null;
        }
        return LocalDate.parse(timeStr, getFormatter(pattern));
    }

    /**
     * String 转LocalDate
     *
     * @param timeStr
     * @param format  时间格式
     * @return LocalDate
     */
    public static LocalDate parseDate(CharSequence timeStr, TimeFormat format) {
        return parseDate(timeStr, format.pattern);
    }

    /**
     * Date 转 LocalDate
     *
     * @param date
     * @return LocalDate
     */
    public static LocalDate parseDate(Date date) {
        if (date == null) {
            return null;
        }
        return instantToZonedDateTime(date.toInstant()).toLocalDate();
    }

    /**
     * long 转 LocalDate
     *
     * @param timestamp 毫秒
     * @return LocalDate
     */
    public static LocalDate parseDate(long timestamp) {
        return longToZonedDateTime(timestamp).toLocalDate();
    }

    /*============================转LocalTime============================*/

    /**
     * String 转LocalTime
     *
     * @param timeStr
     * @return LocalTime
     */
    public static LocalTime parseTime(CharSequence timeStr) {
        if (timeStr == null) {
            return null;
        }
        TimeFormat format = null;
        if (timeStr.length() == 8) {
            format = TimeFormat.TIME_COLON;
        } else if (timeStr.length() == 6) {
            format = TimeFormat.TIME_NONE;
        } else {
            return parseDateTime(timeStr).toLocalTime();
        }
        return parseTime(timeStr, format);
    }

    /**
     * 批量时间转换
     *
     * @param strTimeList
     * @return LocalTime
     */
    public static List<LocalTime> parseTime(List<String> strTimeList) {
        return strTimeList.stream()
                .map(strTime -> parseTime(strTime))
                .collect(Collectors.toList());
    }

    /**
     * String 转LocalTime
     *
     * @param timeStr
     * @param pattern 时间格式
     * @return LocalTime
     */
    public static LocalTime parseTime(CharSequence timeStr, String pattern) {
        if (timeStr == null) {
            return null;
        }
        return LocalTime.parse(timeStr, getFormatter(pattern));
    }

    /**
     * String 转LocalTime
     *
     * @param timeStr
     * @param format  时间格式
     * @return LocalTime
     */
    public static LocalTime parseTime(CharSequence timeStr, TimeFormat format) {
        return parseTime(timeStr, format.pattern);
    }

    /**
     * Date 转 LocalTime
     *
     * @param date
     * @return LocalTime
     */
    public static LocalTime parseTime(Date date) {
        if (date == null) {
            return null;
        }
        return instantToZonedDateTime(date.toInstant()).toLocalTime();
    }

    /**
     * long 转 LocalTime
     *
     * @param timestamp 毫秒
     * @return LocalTime
     */
    public static LocalTime parseTime(long timestamp) {
        return longToZonedDateTime(timestamp).toLocalTime();
    }


    /*============================转long============================*/

    /**
     * LocalDateTime转 long
     *
     * @param time 日期
     * @return long 毫秒
     */
    public static long parseLong(LocalDateTime time) {
        return time.toInstant(defaultZoneOffset()).toEpochMilli();
    }

    /**
     * String转 long
     *
     * @param timeStr 日期字符串
     * @return long 毫秒
     */
    public static long parseLong(CharSequence timeStr) {
        return parseLong(parseDateTime(timeStr));
    }

    /**
     * String 转 long
     *
     * @param timeStr 日期字符串
     * @param format  时间格式
     * @return long 毫秒
     */
    public static long parseLong(CharSequence timeStr, TimeFormat format) {
        return parseLong(parseDateTime(timeStr, format));
    }

    /**
     * String 转 long
     *
     * @param timeStr 日期字符串
     * @param pattern 时间格式
     * @return long 毫秒
     */
    public static long parseLong(CharSequence timeStr, String pattern) {
        return parseLong(parseDateTime(timeStr, pattern));
    }

    /**
     * Date转 long
     *
     * @param date 日期
     * @return long 毫秒
     */
    public static long parseLong(Date date) {
        return parseLong(parseDateTime(date));
    }


    /*============================转String============================*/

    /**
     * 不同String格式互转
     *
     * @param strTime 日期字符串
     * @return String
     */
    public static String parseString(CharSequence strTime) {
        return parseString(strTime, DEFAULT_FORMAT);
    }

    /**
     * 不同String格式互转
     *
     * @param strTime 日期字符串
     * @param pattern 时间格式
     * @return String
     */
    public static String parseString(CharSequence strTime, String pattern) {
        if (strTime == null) {
            return null;
        }
        return getFormatter(pattern).format(parseDateTime(strTime));
    }

    /**
     * 不同String格式互转
     *
     * @param strTime 日期字符串
     * @param format  时间格式
     * @return String
     */
    public static String parseString(CharSequence strTime, TimeFormat format) {
        return parseString(strTime, format.pattern);
    }

    /**
     * LocalDateTime转 String
     *
     * @param time 日期
     * @return String
     */
    public static String parseString(LocalDateTime time) {
        return parseString(time, DEFAULT_FORMAT);
    }

    /**
     * LocalDateTime转 String
     *
     * @param time    日期
     * @param pattern 时间格式
     * @return String
     */
    public static String parseString(LocalDateTime time, String pattern) {
        if (time == null) {
            return null;
        }
        return getFormatter(pattern).format(time);
    }

    /**
     * LocalDateTime转 String
     *
     * @param time   日期
     * @param format 时间格式
     * @return String
     */
    public static String parseString(LocalDateTime time, TimeFormat format) {
        return parseString(time, format.pattern);
    }

    /**
     * long转 String
     *
     * @param timestamp 时间戳
     * @return String
     */
    public static String parseString(long timestamp) {
        return parseString(parseDateTime(timestamp));
    }

    /**
     * long转 String
     *
     * @param timestamp 时间戳
     * @param format    时间格式
     * @return String
     */
    public static String parseString(long timestamp, TimeFormat format) {
        return parseString(parseDateTime(timestamp), format);
    }

    /**
     * long转 String
     *
     * @param timestamp 时间戳
     * @param pattern   时间格式
     * @return String
     */
    public static String parseString(long timestamp, String pattern) {
        return parseString(parseDateTime(timestamp), pattern);
    }

    /**
     * Date 转 String
     *
     * @param date 时间
     * @return String
     */
    public static String parseString(Date date) {
        return parseString(parseDateTime(date));
    }

    /**
     * Date 转 String
     *
     * @param date   时间
     * @param format 时间格式
     * @return String
     */
    public static String parseString(Date date, TimeFormat format) {
        return parseString(parseDateTime(date), format);
    }

    /**
     * Date 转 String
     *
     * @param date    时间
     * @param pattern 时间格式
     * @return String
     */
    public static String parseString(Date date, String pattern) {
        return parseString(parseDateTime(date), pattern);
    }

    /*============================LocalDateTime转其他============================*/

    /**
     * LocalDateTime 转 Date
     *
     * @param localDateTime
     * @return
     */
    public static Date toDate(LocalDateTime localDateTime) {
        return Date.from(toInstant(localDateTime));
    }

    /**
     * LocalDateTime 转 Instant
     *
     * @param localDateTime
     * @return
     */
    public static Instant toInstant(LocalDateTime localDateTime) {
        return localDateTime.toInstant(defaultZoneOffset());
    }


    /*============================获取时间============================*/

    /**
     * 获取当前时间戳（毫秒）
     *
     * @return long 毫秒
     */
    public static long current() {
        return System.currentTimeMillis();
    }

    /**
     * 获取当前时间戳（秒）
     *
     * @return long 秒
     */
    public static long currentSecond() {
        return System.currentTimeMillis() / 1000;
    }

    /**
     * 获取当前时间
     *
     * @return String 当前时间
     */
    public static String now() {
        return now(DEFAULT_FORMAT);
    }

    /**
     * 获取当前时间
     *
     * @param pattern 时间格式
     * @return String 当前时间
     */
    public static String now(String pattern) {
        return getFormatter(pattern).format(LocalDateTime.now());
    }

    /**
     * 获取当前时间
     *
     * @param format 时间格式
     * @return String 当前时间
     */
    public static String now(TimeFormat format) {
        return now(format.pattern);
    }

    /**
     * 获取当前日期
     *
     * @return String 当前日期
     */
    public static String today() {
        return now(TimeFormat.DATE_LINE);
    }


    /**
     * 获取日期时间转换器，内部方法
     */
    private static DateTimeFormatter getFormatter(String format) {
        return new DateTimeFormatterBuilder()
                .appendPattern(format)
                .parseDefaulting(ChronoField.YEAR_OF_ERA, 1970)
                .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
                .parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
                .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
                .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
                .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
                .toFormatter();
    }


    /**
     * 分隔符
     */
    public enum Separation {
        LINE("-"),
        SLASH("/"),
        BACKSLASH("\\"),
        COLON(":"),
        ;
        private transient String separation;

        Separation(String separation) {
            this.separation = separation;
        }
    }


    /**
     * 时间格式
     */
    public enum TimeFormat {

        /**
         * 格式：年-月
         */
        DATE_MONTH_LINE("yyyy-MM"),
        DATE_MONTH_SLASH("yyyy/MM"),
        DATE_MONTH_BACKSLASH("yyyy\\MM"),
        DATE_MONTH_NONE("yyyyMM"),

        /**
         * 日期格式
         */
        DATE_LINE("yyyy-MM-dd"),
        DATE_SLASH("yyyy/MM/dd"),
        DATE_BACKSLASH("yyyy\\MM\\dd"),
        DATE_CN("yyyy年MM月dd日"),
        DATE_NONE("yyyyMMdd"),

        /**
         * 时间格式
         */
        TIME_COLON("HH:mm:ss"),
        TIME_NONE("HHmmss"),

        /**
         * 日期时间格式
         */
        DATETIME_LINE("yyyy-MM-dd HH:mm:ss"),
        DATETIME_SLASH("yyyy/MM/dd HH:mm:ss"),
        DATETIME_BACKSLASH("yyyy\\MM\\dd HH:mm:ss"),
        DATETIME_CN("yyyy年MM月dd日HH时mm分ss秒"),
        DATETIME_NONE("yyyyMMddHHmmss"),

        /**
         * 日期时间格式 带毫秒
         */
        DATETIME_MS_LINE("yyyy-MM-dd HH:mm:ss.SSS"),
        DATETIME_MS_SLASH("yyyy/MM/dd HH:mm:ss.SSS"),
        DATETIME_MS_BACKSLASH("yyyy\\MM\\dd HH:mm:ss.SSS"),
        DATETIME_MS_NONE("yyyyMMddHHmmssSSS"),

        /**
         * ISO8601日期时间格式，精确到毫秒：yyyy-MM-dd HH:mm:ss,SSS
         */
        DATETIME_ISO("yyyy-MM-dd HH:mm:ss,SSS"),

        /**
         * HTTP头中日期时间格式：EEE, dd MMM yyyy HH:mm:ss z
         */
        DATETIME_HTTP("EEE, dd MMM yyyy HH:mm:ss z"),

        /**
         * JDK中日期时间格式：EEE MMM dd HH:mm:ss zzz yyyy
         */
        DATETIME_JDK("EEE MMM dd HH:mm:ss zzz yyyy"),

        /**
         * UTC时间：yyyy-MM-dd'T'HH:mm:ss
         */
        DATETIME_UTC("yyyy-MM-dd'T'HH:mm:ss"),
        ;

        private transient String pattern;

        TimeFormat(String pattern) {
            this.pattern = pattern;
        }

        public boolean equals(String str) {
            return this.pattern.equals(str);
        }

    }
}